use std::num::ParseIntError;

pub fn time_info(time: &str) -> String {
    let date = Date::try_from(time).expect("输入格式有错误");
    format!(
        "{},{},{}",
        date.get_nth_week(),
        date.left_days(),
        date.days_to_spring_festival()
    )
}

#[derive(Debug, Clone)]
struct Date {
    leap_year: bool,
    year: u32,
    month: u32,
    day: u32,
}

impl Date {
    const TOTAL_DAYS_MONTH: [u32; 13] =
        [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365];
    const LUNAR199Y: [u32; 199] = [
        0x04AE53, 0x0A5748, 0x5526BD, 0x0D2650, 0x0D9544, 0x46AAB9, 0x056A4D, 0x09AD42, 0x24AEB6,
        0x04AE4A, /*1901-1910*/
        0x6A4DBE, 0x0A4D52, 0x0D2546, 0x5D52BA, 0x0B544E, 0x0D6A43, 0x296D37, 0x095B4B, 0x749BC1,
        0x049754, /*1911-1920*/
        0x0A4B48, 0x5B25BC, 0x06A550, 0x06D445, 0x4ADAB8, 0x02B64D, 0x095742, 0x2497B7, 0x04974A,
        0x664B3E, /*1921-1930*/
        0x0D4A51, 0x0EA546, 0x56D4BA, 0x05AD4E, 0x02B644, 0x393738, 0x092E4B, 0x7C96BF, 0x0C9553,
        0x0D4A48, /*1931-1940*/
        0x6DA53B, 0x0B554F, 0x056A45, 0x4AADB9, 0x025D4D, 0x092D42, 0x2C95B6, 0x0A954A, 0x7B4ABD,
        0x06CA51, /*1941-1950*/
        0x0B5546, 0x555ABB, 0x04DA4E, 0x0A5B43, 0x352BB8, 0x052B4C, 0x8A953F, 0x0E9552, 0x06AA48,
        0x6AD53C, /*1951-1960*/
        0x0AB54F, 0x04B645, 0x4A5739, 0x0A574D, 0x052642, 0x3E9335, 0x0D9549, 0x75AABE, 0x056A51,
        0x096D46, /*1961-1970*/
        0x54AEBB, 0x04AD4F, 0x0A4D43, 0x4D26B7, 0x0D254B, 0x8D52BF, 0x0B5452, 0x0B6A47, 0x696D3C,
        0x095B50, /*1971-1980*/
        0x049B45, 0x4A4BB9, 0x0A4B4D, 0xAB25C2, 0x06A554, 0x06D449, 0x6ADA3D, 0x0AB651, 0x093746,
        0x5497BB, /*1981-1990*/
        0x04974F, 0x064B44, 0x36A537, 0x0EA54A, 0x86B2BF, 0x05AC53, 0x0AB647, 0x5936BC, 0x092E50,
        0x0C9645, /*1991-2000*/
        0x4D4AB8, 0x0D4A4C, 0x0DA541, 0x25AAB6, 0x056A49, 0x7AADBD, 0x025D52, 0x092D47, 0x5C95BA,
        0x0A954E, /*2001-2010*/
        0x0B4A43, 0x4B5537, 0x0AD54A, 0x955ABF, 0x04BA53, 0x0A5B48, 0x652BBC, 0x052B50, 0x0A9345,
        0x474AB9, /*2011-2020*/
        0x06AA4C, 0x0AD541, 0x24DAB6, 0x04B64A, 0x69573D, 0x0A4E51, 0x0D2646, 0x5E933A, 0x0D534D,
        0x05AA43, /*2021-2030*/
        0x36B537, 0x096D4B, 0xB4AEBF, 0x04AD53, 0x0A4D48, 0x6D25BC, 0x0D254F, 0x0D5244, 0x5DAA38,
        0x0B5A4C, /*2031-2040*/
        0x056D41, 0x24ADB6, 0x049B4A, 0x7A4BBE, 0x0A4B51, 0x0AA546, 0x5B52BA, 0x06D24E, 0x0ADA42,
        0x355B37, /*2041-2050*/
        0x09374B, 0x8497C1, 0x049753, 0x064B48, 0x66A53C, 0x0EA54F, 0x06B244, 0x4AB638, 0x0AAE4C,
        0x092E42, /*2051-2060*/
        0x3C9735, 0x0C9649, 0x7D4ABD, 0x0D4A51, 0x0DA545, 0x55AABA, 0x056A4E, 0x0A6D43, 0x452EB7,
        0x052D4B, /*2061-2070*/
        0x8A95BF, 0x0A9553, 0x0B4A47, 0x6B553B, 0x0AD54F, 0x055A45, 0x4A5D38, 0x0A5B4C, 0x052B42,
        0x3A93B6, /*2071-2080*/
        0x069349, 0x7729BD, 0x06AA51, 0x0AD546, 0x54DABA, 0x04B64E, 0x0A5743, 0x452738, 0x0D264A,
        0x8E933E, /*2081-2090*/
        0x0D5252, 0x0DAA47, 0x66B53B, 0x056D4F, 0x04AE45, 0x4A4EB9, 0x0A4D4C, 0x0D1541,
        0x2D92B5, /*2091-2099*/
    ];
    /// 当前是全年的第几周， 某年的最后一周如果出现了跨年的问题的话，>=星期四,算是本年的周数
    /// <星期四,周数算是下一年的第一周
    #[inline(always)]
    pub fn get_nth_week(&self) -> u32 {
        let day_of_week_1m1d = Self::new_year_day(self.year).day_of_week(); // 1月1号星期几
        let day_of_week_last_day = Self::last_day(self.year).day_of_week(); // 1月31号星期几
        let day_of_week_current_day = self.day_of_week();
        // 判断当前的所在周跨年了没有,当月是12月，日历号大于星期号，self.day - 星期号 = 星期日的日历号， 然后
        // 日历号+7就是下个周日的日历，如果大于31就是跨年了，如果最后次年的最后一天的星期数小于4,则当前是下一年
        // 的第一周
        if self.month == 12
            && self.day >= day_of_week_current_day
            && self.day - day_of_week_current_day + 7 > 31
            && day_of_week_last_day < 4
        {
            return 1;
        }

        let start_off = if day_of_week_1m1d < 4 { 1 } else { 0 }; // 年初是否算一周
        if self.past_days() < (7 - day_of_week_1m1d + 1) {
            // 这里返回第一周，获取0(表示上一年的最后一周的数值)
            return start_off;
        }
        let start = self.past_days() - (7 - day_of_week_1m1d + 1); // 去掉开始开头，从周一开始，每七天一个周末
        let weeks = start / 7;
        let weeks = weeks + if start % 7 != 0 { 1 } else { 0 };
        weeks + start_off
    }

    // 利用蔡勒公式获取星期几，7：星期日  1：星期1 ...
    #[inline(always)]
    pub fn day_of_week(&self) -> u32 {
        let (y, m) = if self.month < 3 {
            (self.year - 1, self.month + 12)
        } else {
            (self.year, self.month)
        };
        let c = y / 100;
        let y = y % 100;
        let d = self.day;
        let w = y + y / 4 + c / 4 + 26 * (m + 1) / 10 + d - 1 - 2 * c;
        let ret = w % 7;
        if ret == 0 {
            7
        } else {
            ret
        }
    }

    /// 全年还剩下多少天
    #[inline(always)]
    pub fn left_days(&self) -> u32 {
        self.count_days_one_year() - self.past_days()
    }

    /// 这一年的总天数
    #[inline(always)]
    pub fn count_days_one_year(&self) -> u32 {
        if self.leap_year {
            366
        } else {
            365
        }
    }

    /// 到目前为止流失了多少天了
    #[inline(always)]
    pub fn past_days(&self) -> u32 {
        let day = if self.leap_year { 1u32 } else { 0 };
        Self::TOTAL_DAYS_MONTH[(self.month - 1) as usize]
            + self.day
            + if self.month < 3 { 0 } else { day }
    }

    /// 距离春节还有多少天
    #[inline(always)]
    pub fn days_to_spring_festival(&self) -> u32 {
        let mut solar = Self::string_fesival(self.year).lunar_to_solar();
        if solar.year > self.year {
            return self.left_days() + solar.past_days() - 1;
        }

        if solar.past_days() - 1 < self.past_days() {
            // 今年的春节已过
            solar = Self::string_fesival(self.year + 1).lunar_to_solar();
            self.left_days() + solar.past_days() - 1
        } else {
            solar.past_days() - 1 - self.past_days()
        }
    }

    /// 阴历转阳历
    /// Date::try_from("2026-2-1").unwrap().lunar_to_solar();
    /// 调用后，生成一个Date {year: 2026, month: 3, day: 19, leap_year: false} 阴历2026的2月初一是阳历的2026年3月19日
    ///    
    pub fn lunar_to_solar(&self) -> Self {
        let Date {
            mut year,
            mut month,
            day,
            leap_year,
        } = *self;

        let mut by_now = (Self::LUNAR199Y[(year - 1901) as usize] & 0x001F) - 1;

        if ((Self::LUNAR199Y[(year - 1901) as usize] & 0x0060) >> 5) == 2 {
            by_now += 31;
        }

        for i in 1..month {
            if (Self::LUNAR199Y[(year - 1901) as usize] & (0x80000 >> (i - 1))) == 0 {
                by_now += 29;
            } else {
                by_now += 30;
            }
        }
        by_now += day;
        let x_month = (Self::LUNAR199Y[(year - 1901) as usize] & 0xf00000) >> 20;
        if x_month != 0 {
            if month > x_month || (month == x_month && leap_year) {
                if (Self::LUNAR199Y[(year - 1901) as usize] & (0x80000 >> (month - 1))) == 0 {
                    by_now += 29;
                } else {
                    by_now += 30;
                }
            }
        }
        if by_now > 366 || (year % 4 != 0 && by_now == 365) {
            year += 1;
            if year % 4 == 0 {
                by_now -= 366;
            } else {
                by_now -= 365;
            }
        }
        for i in 1..13 {
            if Self::TOTAL_DAYS_MONTH[i] >= by_now {
                month = (i) as u32;
                break;
            }
        }

        Self {
            year,
            month,
            day: by_now - Self::TOTAL_DAYS_MONTH[(month - 1) as usize],
            leap_year: is_leap_year(year),
        }
    }

    /// 阳历转阴历
    /// Date::try_from("2026-3-19").unwrap().solar_to_lunar();
    /// 调用后，生成一个Date {year: 2026, month: 2, day: 1, leap_year: false} 阳历2026的3月19日是阴历的2026年2月初一
    pub fn solar_to_lunar(&self) -> Self {
        let Date {
            mut year,
            mut month,
            mut day,
            ..
        } = *self;

        let mut index = 1u32; // 记录从哪个月开始来计算。
        let mut flag = false; // 是用来对闰月的特殊处理。

        // 记录春节离当年元旦的天数
        let mut by_spring = if ((Self::LUNAR199Y[(year - 1901) as usize] & 0x0060) >> 5) == 1 {
            (Self::LUNAR199Y[(year - 1901) as usize] & 0x001F) - 1
        } else {
            (Self::LUNAR199Y[(year - 1901) as usize] & 0x001F) - 1 + 31
        };

        // 记录阳历日离当年元旦的天数
        let mut by_solar = Self::TOTAL_DAYS_MONTH[(month - 1) as usize] + day - 1;
        if year % 4 == 0 && month > 2 {
            by_solar += 1;
        }

        //判断阳历日在春节前还是春节后
        if by_solar >= by_spring {
            //阳历日在春节后（含春节那天）
            by_solar -= by_spring;
            month = 1;
            // 记录大小月的天数 29 或30
            let mut days_per_month =
                if (Self::LUNAR199Y[(year - 1901) as usize] & (0x80000 >> index - 1)) == 0 {
                    29
                } else {
                    30
                };

            while by_solar >= days_per_month {
                by_solar -= days_per_month;
                index += 1;
                if month == (Self::LUNAR199Y[(year - 1901) as usize] & 0xF00000) >> 20 {
                    flag = !flag;
                    if !flag {
                        month += 1;
                    }
                } else {
                    month += 1;
                }

                if Self::LUNAR199Y[(year - 1901) as usize] & (0x80000 >> index - 1) == 0 {
                    days_per_month = 29;
                } else {
                    days_per_month = 30;
                }
            }
            day = by_solar + 1;
        } else {
            //阳历日在春节前
            by_spring -= by_solar;
            year -= 1;
            month = 12;
            if (Self::LUNAR199Y[(year - 1901) as usize] & 0xF00000) >> 20 == 0 {
                index = 12;
            } else {
                index = 13;
            }

            flag = false;
            let mut days_per_month =
                if Self::LUNAR199Y[(year - 1901) as usize] & (0x80000 >> index - 1) == 0 {
                    29
                } else {
                    30
                };

            while by_spring > days_per_month {
                by_spring -= days_per_month;
                index -= 1;
                if !flag {
                    month -= 1;
                }

                if month == (Self::LUNAR199Y[(year - 1901) as usize] & 0xF00000) >> 20 {
                    flag = !flag;
                }

                if Self::LUNAR199Y[(year - 1901) as usize] & (0x80000 >> index - 1) == 0 {
                    days_per_month = 29;
                } else {
                    days_per_month = 30;
                }
            }

            day = days_per_month - by_spring + 1;
        }

        let leap_year = if month == (Self::LUNAR199Y[(year - 1901) as usize] & 0xF00000) >> 20 {
            true
        } else {
            false
        };
        Date {
            leap_year,
            year,
            month,
            day,
        }
    }

    // 建立一个春节的日期
    #[inline(always)]
    fn string_fesival(year: u32) -> Self {
        Self {
            leap_year: is_leap_year(year),
            year,
            month: 1,
            day: 1,
        }
    }

    // 元旦
    #[inline(always)]
    fn new_year_day(year: u32) -> Self {
        Self::string_fesival(year)
    }

    // 某年的最后一天
    #[inline(always)]
    fn last_day(year: u32) -> Self {
        Self {
            leap_year: is_leap_year(year),
            year,
            month: 12,
            day: 31,
        }
    }
}

impl TryFrom<&str> for Date {
    type Error = String;

    #[inline(always)]
    fn try_from(time: &str) -> Result<Self, Self::Error> {
        let time: Result<Vec<u32>, ParseIntError> =
            time.trim().split('-').map(|t| t.parse::<u32>()).collect();
        let time = time.map_err(|e| e.to_string())?;
        if time.len() != 3 {
            return Err(String::from("输入有错误"));
        }
        if !(1901..=2098).contains(&time[0]) {
            return Err(String::from("输入的年份范围应该在1901至2098之间"));
        }

        if !(1u32..=12).contains(&time[1]) {
            return Err(String::from("输入月份有错误"));
        }

        let is_leap = is_leap_year(time[0]);
        let max = match time[1] {
            1 | 3 | 5 | 7 | 8 | 10 | 12 => 31,
            2 => {
                if is_leap {
                    29
                } else {
                    28
                }
            }
            4 | 6 | 9 | 11 => 30,
            _ => return Err(String::from("输入的月份数值有误")),
        };
        if !(1..=max).contains(&time[2]) {
            return Err(String::from("输入的日数值有错误"));
        }

        Ok(Date {
            year: time[0],
            month: time[1],
            day: time[2],
            leap_year: is_leap,
        })
    }
}

// 判断是否是闰年
fn is_leap_year(year: u32) -> bool {
    if year % 400 == 0 {
        return true;
    }

    if year % 4 == 0 && year % 100 != 0 {
        return true;
    }
    false
}
